package getui

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
)

// Client is the client of getui API V2
type Client struct {
	appId        string
	key          string
	secret       string
	masterSecret string
	entrypoint   string
}

// NewClient returns a new client entity
func NewClient(appId, key, secret, masterSecret string) Client {
	return Client{
		appId:        appId,
		key:          key,
		secret:       secret,
		masterSecret: masterSecret,
		entrypoint:   fmt.Sprintf("https://restapi.getui.com/v2/%s", appId),
	}
}

// GetToken gets a new token.
func (c Client) GetToken() (TokenResponse, error) {
	var tr TokenResponse
	req := TokenRequest{
		AppKey: c.key,
	}
	req.Sign, req.Timestamp = signature(c.key, c.masterSecret)
	p, err := json.Marshal(req)
	if err != nil {
		return tr, err
	}
	body, err := sendPost(c.entrypoint+"/auth", "", p)
	if err != nil {
		return tr, err
	}
	if len(body) == 0 {
		return tr, errors.New("no response data")
	}
	err = json.Unmarshal(body, &tr)
	if err != nil {
		return tr, err
	}

	return tr, nil
}

func (c Client) BindAlias(ctx context.Context, token string, cas ...BindAliasData) (BindAliasResponse, error) {
	var sr BindAliasResponse
	if len(cas) == 0 {
		return sr, errors.New("至少需要输入一个参数对")
	}
	if len(cas) > 1000 {
		return sr, errors.New("最多输入1000个参数对")
	}
	p, err := json.Marshal(struct {
		DataList []BindAliasData `json:"data_list"`
	}{
		DataList: cas,
	})
	if err != nil {
		return sr, err
	}
	body, err := sendPost(c.entrypoint+"/user/alias", token, p)
	if err != nil {
		return sr, err
	}
	if len(body) == 0 {
		return sr, errors.New("no response data")
	}
	err = json.Unmarshal(body, &sr)
	if err != nil {
		return sr, err
	}
	return sr, nil
}

// PushToSingleByCID pushes a msg to single user by cid
func (c Client) PushToSingleByCID(token string, req SingleRequest) (SingleResponse, error) {
	url := c.entrypoint + "/push/single/cid"

	return c.pushToSingle(token, url, req)
}

// PushToSingleByCID pushes a msg to single user by alias
func (c Client) PushToSingleByAlias(token string, req SingleRequest) (SingleResponse, error) {
	url := c.entrypoint + "/push/single/alias"

	return c.pushToSingle(token, url, req)
}

// PushToSingleBatchByCID (batches) pushes a msg to single user by cid
func (c Client) PushToSingleBatchByCID(token string, req SingleBatchRequest) (SingleResponse, error) {
	url := c.entrypoint + "/push/single/batch/cid"

	return c.pushToSingleBatch(token, url, req)
}

// PushToSingleBatchByAlias (batches) pushes a msg to single user by alias
func (c Client) PushToSingleBatchByAlias(token string, req SingleBatchRequest) (SingleResponse, error) {
	url := c.entrypoint + "/push/single/batch/alias"

	return c.pushToSingleBatch(token, url, req)
}

// pushToSingle pushes a msg to single user
func (c Client) pushToSingle(token, url string, req SingleRequest) (SingleResponse, error) {
	var sr SingleResponse
	p, err := json.Marshal(req)
	if err != nil {
		return sr, err
	}

	body, err := c.push(token, url, p)
	if err != nil {
		return sr, err
	}
	err = json.Unmarshal(body, &sr)
	if err != nil {
		return sr, err
	}

	return sr, nil
}

// pushToSingleBatch pushes a msg to single user
func (c Client) pushToSingleBatch(token, url string, req SingleBatchRequest) (SingleResponse, error) {
	var sr SingleResponse
	p, err := json.Marshal(req)
	if err != nil {
		return sr, err
	}

	body, err := c.push(token, url, p)
	if err != nil {
		return sr, err
	}
	err = json.Unmarshal(body, &sr)
	if err != nil {
		return sr, err
	}

	return sr, nil
}

// PushToAll pushes a msg to all users
func (c Client) PushToAll(token string, req AllRequest) (AllResponse, error) {
	var sr AllResponse
	p, err := json.Marshal(req)
	if err != nil {
		return sr, err
	}

	url := c.entrypoint + "/push/all"
	body, err := c.push(token, url, p)
	if err != nil {
		return sr, err
	}
	err = json.Unmarshal(body, &sr)
	if err != nil {
		return sr, err
	}

	return sr, nil
}

// push pushes a msg to single user
func (c Client) push(token, url string, body []byte) ([]byte, error) {
	body, err := sendPost(url, token, body)
	if err != nil {
		return nil, err
	}
	if len(body) == 0 {
		return nil, errors.New("no response data")
	}
	return body, nil
}
